package utils

import (
	"context"
	"sync"
	"time"

	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
)

type Cache struct {
	localCache  *sync.Map // 本地缓存（键为 string，值为 (value, expiration)）
	redisClient *redis.Client
}

func NewCache(redisClient *redis.Client) *Cache {
	return &Cache{
		localCache:  &sync.Map{},
		redisClient: redisClient,
	}
}

func (c *Cache) Get(key string) (string, bool) {
	// 从本地缓存获取
	if val, ok := c.localCache.Load(key); ok {
		// 正确断言结构体类型并提取字段
		if v, ok := val.(struct {
			Value      string
			Expiration time.Time
		}); ok {
			if time.Now().Before(v.Expiration) {
				return v.Value, true
			}
		}
		c.localCache.Delete(key) // 过期则删除
	}

	// 从 Redis 获取
	val, err := c.redisClient.Get(context.Background(), key).Result()
	if err == redis.Nil {
		return "", false
	} else if err != nil {
		zap.L().Error("Redis 获取缓存失败", zap.Error(err))
		return "", false
	}

	// 设置本地缓存（包含过期时间）
	ttl, _ := c.redisClient.TTL(context.Background(), key).Result()
	expiration := time.Now().Add(ttl)
	c.localCache.Store(key, struct {
		Value      string
		Expiration time.Time
	}{val, expiration})

	return val, true
}

func (c *Cache) Set(key string, value string, ttl time.Duration) {
	ctx := context.Background()
	// 写入 Redis
	err := c.redisClient.Set(ctx, key, value, ttl).Err()
	if err != nil {
		zap.L().Error("Redis 设置缓存失败", zap.Error(err))
	}

	// 写入本地缓存（包含过期时间）
	expiration := time.Now().Add(ttl)
	c.localCache.Store(key, struct {
		Value      string
		Expiration time.Time
	}{value, expiration})
}

// 懒加载方式清理过期缓存
func (c *Cache) StartGC(interval time.Duration) {
	ticker := time.NewTicker(interval)
	go func() {
		for range ticker.C {
			c.localCache.Range(func(k, v interface{}) bool {
				entry := v.(struct{ Expiration time.Time })
				if time.Now().After(entry.Expiration) {
					c.localCache.Delete(k)
				}
				return true
			})
		}
	}()
}
